<?php
    /*
        // $db = \WZ_Flatdb::getInstance('user');
            // $users = $db->select('age <= 51');
            // if (is_array($users)) {
                // foreach ($users as $user) {
                    // $age = $user->getAge();
                    // if ($age > 34) {
                        // $user->delete();
                    // }
                // }
            // } else if (is_object($users)) {
                // $user = $users;
                // $age = $user->getAge();
                // if ($age > 34) {
                    // $user->delete();
                // } else {
                    // var_dump($age);
                // }
            // }
            // $user = $db->newRow();
            // $user->setName('plusquellec');
            // $user->setFirstname('gerald');
            // $user->setAge(40);
            // $user->save();
    */
    
    class WZ_Flatdb
    {
        static $_instance;
        static $_data;
        static $_db;
        static $_flatFile;
        static $_row;
        static $_rows;
        static $_cursor = false;
		
		// singleton
		public static function &getInstance($db = 'flatedb') 
		{
            if (!self::$_instance instanceof self) {
				self::$_instance = new self($db);
			}
            return self::$_instance;
		}
        
        public function __construct($db)
        {
            if (empty($db)) {
                throw new \WZ_Exception('The WZ_Flatdb class needs a file name');
            }
            $flatFile = DIR_FLATDB . $db . '.flatdb';
            if (!file_exists($flatFile)) {
                touch($flatFile, 0777);
            }
            self::$_db = $db;
            self::$_flatFile = $flatFile;
            self::$_data = unserialize(file_get_contents($flatFile));
        }
        
        public static function getData()
        {
            return self::$_data;
        }
        
        public function __call($method, $args)
        {
            $methodCalled = substr($method, 0, 3);
            $key = strtolower(\WZ_Inflector::uncamelize(lcfirst(substr($method, 3))));
            
            if ($methodCalled == 'get') {
                return self::getField($key);
            } elseif ($methodCalled == 'get') {
                return self::setField($key, $args[0]);
            }
        }
        
        public static function setField($key, $value)
        {
            if (false !== self::$_cursor) {
                self::$_row = self::getRow();
                self::$_row[$key] = $value;
            }
            return false;
        }
        
        public static function newRow()
        {
            $lastId = self::getLastId();
            self::$_cursor = $lastId + 1;
            $obj = new \WZ_FlatObject;
            $obj->_db = self::getDb();
            $obj->_id = self::$_cursor;
            return $obj;
        }
        
        public static function getField($key)
        {
            if (false !== self::$_cursor) {
                self::$_row = self::getRow();
                if (isset(self::$_row[$key])) {
                    return self::$_row[$key];
                }
            }
            return null;
        }
        
        public static function getRow()
        {
            if (null !== self::$_row) {
                return self::$_row;
            }
            if (false !== self::$_cursor) {
                if (isset(self::$_data[self::$_cursor])) {
                    return self::$_data[self::$_cursor];
                } else {
                    return array();
                }
            }
            return null;
        }
        
        public static function getLastId()
        {
            $rows = self::select();
            if (!count($rows)) {
                return 0;
            } else {
                $maxId = 0;
                foreach ($rows as $row) {
                    $_id = $row->_id;
                    if ($_id > $maxId) {
                        $maxId = $_id;
                    }
                }
                return $maxId;
            }
        }
        
        public static function select($where = null, $offset, $limit = false)
        {
            $collection = array();
            if (is_array(self::$_data)) {
                foreach (self::$_data as $idRow => $dataRow) {
                    $collection[$idRow] = self::find($idRow);
                }
                if (count($collection) && null !== $where) {
                    $where = str_replace('NOT LIKE', 'NOTLIKE', $where);
                    $where = str_replace('NOT IN', 'NOTIN', $where);
                    if (!strstr($where, ' && ') && !strstr($where, ' || ')) {
                        list($field, $op, $value) = explode(' ', $where);
                        $tmp = array();
                        foreach ($collection as $idRow => $obj) {
                            if (true === self::analyze($obj, $field, $op, $value)) {
                                $tmp[] = $obj; 
                                self::$_cursor = $idRow;
                            }
                        }
                        if (count($tmp) == 1) {
                            return $tmp[0];
                        } else if (count($tmp) > 1) {
                            return $tmp;
                        } elseif (count($tmp) < 1) {
                            return array();
                        }
                    }
                    
                    $newQuery = $where;
                    $tabAnd = explode(' && ', $where);
                    $return = array();
                    
                    foreach ($tabAnd as $subQuery) {
                        $newQuery = str_replace($subQuery . ' && ', '', $newQuery);
                    }
                    
                    $tabOr = explode(' || ', $newQuery);
                    
                    if (count($tabAnd) > 1) {
                        $resAnd = array();
                        foreach ($tabAnd as $subQuery) {
                            list($field, $op, $value) = explode(' ', $subQuery);
                            $tmp = array();
                            foreach ($collection as $idRow => $obj) {
                                if (true === self::analyze($obj, $field, $op, $value)) {
                                    $tmp[] = $id;  
                                    self::$_cursor = $idRow;
                                }
                            }
                            if (count($resAnd)) {
                                $intersection = array_intersect($resAnd, $tmp);
                                $resAnd = $intersection;
                            } else {
                                $resAnd = $tmp;
                            }
                        }
                        $return = array_merge($return, $resAnd);
                    }
                    
                    if (count($tabOr) > 1) {
                        $resOr = array();
                        foreach ($tabOr as $subQuery) {
                            list($field, $op, $value) = explode(' ', $subQuery);
                            $tmp = array();
                            foreach ($collection as $idRow => $obj) {
                                if (true === self::analyze($obj, $field, $op, $value))
                                    $tmp[] = $id; 
                                    self::$_cursor = $idRow;
                            }
                            $resOr = array_merge($resOr, $tmp);
                        }
                        $return = array_merge($return, $resOr);
                    }
                    
                    if (count($return)) {
                        return array_unique($return);
                    }
                }
            }
            if (count($collection) == 1) {
                return $collection[0];
            } else if (count($collection) > 1) {
                return $collection;
            } elseif (count($collection) < 1) {
                return array();
            }
        }
        
        public static function analyze($obj, $field, $op, $value)
		{
            $comp = $obj->$field;
			if (isset($comp)) {
				$comp = strtolower($comp);
				$value = strtolower($value);
				switch ($op) {
					case '=':
						if ($comp == $value) {
							return true;
						}
                        break;
					case '>=':
						if ($comp >= $value) {
							return true;
						}
                        break;
					case '>':
						if ($comp > $value) {
							return true;
						}
                        break;
					case '<':
						if ($comp < $value) {
							return true;
						}
                        break;
					case '<=':
						if ($comp <= $value) {
							return true;
						}
                        break;
					case '<>':
					case '!=':
						if ($comp <> $value) {
							return true;
						}
                        break;
					case 'LIKE':
                        $value = str_replace('"', '', $value);
                        $value = str_replace('%', '', $value);
                        if (strstr($comp, $value)) {
							return true;
						}
                        break;
					case 'NOTLIKE':
                        $value = str_replace('"', '', $value);
                        $value = str_replace('%', '', $value);
                        if (!strstr($comp, $value)) {
							return true;
						}
                        break;
					case 'IN':
						$value = str_replace('(', '', $value);
						$value = str_replace(')', '', $value);
						$tabValues = explode(',', $value);
						if (in_array($comp, $tabValues)) {
							return true;
						}
                        break;
					case 'NOTIN':
						$value = str_replace('(', '', $value);
						$value = str_replace(')', '', $value);
						$tabValues = explode(',', $value);
						if (!in_array($comp, $tabValues)) {
							return true;
						}
                        break;
				}
			}
			return false;
		}
        
        public static function save()
        {
            if (false !== self::$_cursor) {
                self::delete();
                self::$_data[] = self::$_row;
                self::renewData();
            }
            return false;
        }
        
        public static function delete()
        {
            if (false !== self::$_cursor) {
                if (isset(self::$_data[self::$_cursor])) {
                    unset(self::$_data[self::$_cursor]);
                    self::renewData();
                    return true;
                }
            }
            return false;
        }
        
        public static function renewData()
        {
            $data = serialize(self::$_data);
            unlink(self::$_flatFile);
            touch(self::$_flatFile, 0777);
            $fp = fopen(self::$_flatFile, 'a');
            fwrite($fp, $data);
            fclose($fp);
            self::$_data = unserialize(file_get_contents(self::$_flatFile));
        }
        
        public static function getDb()
        {
            return self::$_db;
        }
        
        public static function findById($id)
        {
            $row = self::select('_id = ' . $id);
            return $row;
        }
        
        public static function find($id)
        {
            if (isset(self::$_data[$id])) {
                $obj = new \WZ_FlatObject;
                foreach (self::$_data[$id] as $key => $value) {
                    $obj->$key = $value;
                }
                return $obj;
            }
            return null;
        }
    }
    
    class WZ_FlatObject
    {
        static $_db;
        static $_id;
        
        public function save()
        {
            $db = self::getDb();
            $vars = get_object_vars($this);
            unset($vars['_db']);
            foreach ($vars as $key => $value) {
                $db::setField($key, $value);
            }
            $db::save();
            return $this;
        }
        
        public function __call($method, $args)
        {
            $meth = substr($method, 0, 3);
            if ($meth == 'get') {
                $var = strtolower(substr($method, 3));
                return self::get($var);
            } elseif ($meth == 'set') {
                $var = strtolower(substr($method, 3));
                return self::set($var, $args[0]);
            }
        }
        
        public function delete()
        {
            $db = self::getDb();
            $row = $db::findById($this->_id);
            return $db::delete();
        }
        
        public function set($key, $value)
        {
            $this->$key = $value;
        }
        
        public function get($key)
        {
            if (isset($this->$key)) {
                return $this->$key;
            }
            return null;
        }
        
        public static function getDb()
        {
            return \WZ_Flatdb::getInstance(self::$_db);
        }
    }